package top.bug.blog.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.bug.blog.common.BasePO;
import top.bug.blog.constant.ArticleConstants;
import top.bug.blog.constant.CommonConstants;
import top.bug.blog.entity.dto.article.*;
import top.bug.blog.entity.dto.common.BatchRequest;
import top.bug.blog.entity.dto.common.LikeRequest;
import top.bug.blog.entity.dto.common.OrderModRequest;
import top.bug.blog.entity.dto.common.SingleRequest;
import top.bug.blog.entity.po.*;
import top.bug.blog.entity.vo.article.ArticleCardVO;
import top.bug.blog.entity.vo.article.ArticleVO;
import top.bug.blog.entity.vo.user.SafeUserVO;
import top.bug.blog.enums.ErrorCode;
import top.bug.blog.enums.LikeEnum;
import top.bug.blog.exception.BussinessException;
import top.bug.blog.mapper.*;
import top.bug.blog.service.ArticleService;
import top.bug.blog.service.LikeService;
import top.bug.blog.utils.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static top.bug.blog.constant.CommonConstants.STATUS_ENABLE;
import static top.bug.blog.utils.StrUtils.isNotBlank;

/**
 * @author <a href="https://github.com/jiusi66">九思.</a>
 * @email 10837907@qq.com
 * @from <a href="https://www.52bug.top">递归茶馆-www.52bug.top</a>
 * @description 文章 实现类
 */
@Service
@Slf4j
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {
    @Resource
    private ArticleMapper articleMapper;
    @Resource
    private ArticleContentMapper articleContentMapper;
    @Resource
    private UserMapper userMapper;
    @Resource
    private CategoryMapper categoryMapper;
    @Resource
    private TagMapper tagMapper;
    @Resource
    private ArticleTagMapper articleTagMapper;
    @Resource
    private CommentsMapper commentsMapper;
    @Resource
    HttpServletRequest httpServletRequest;
    @Resource
    private LikeService likeService;

    @Value("${openai.api-key}")
    private String apiKey;
    // region 后台接口

    @Override
    public ArticleVO details(SingleRequest singleRequest) {
        if (null == singleRequest || StrUtil.isEmpty(singleRequest.getId())) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        String id = singleRequest.getId();
        Article article = this.getById(id);
        ArticleVO vo = new ArticleVO();
        BeanUtil.copyProperties(article, vo);
        // 获取文章内容
        ArticleContent content = articleContentMapper.selectOne(new LambdaQueryWrapper<ArticleContent>()
                .eq(ArticleContent::getArticleId, id));
        if (null != content) {
            vo.setContent(content.getContent());
        }
        // 获取标签信息
        List<Tag> tags = articleTagMapper.selectList(new LambdaQueryWrapper<ArticleTag>()
                        .eq(ArticleTag::getArticleId, id)).stream()
                .map(item -> tagMapper.selectById(item.getTagId())).filter(Objects::nonNull)
                .collect(Collectors.toList());
        // 填充作者、分类信息
        filleInfo2VO(article, vo);
        vo.setTags(tags);
        // todo： 更新文章访问数
        return vo;
    }

    @Override
    public Page<ArticleVO> getList(ArticleQueryRequest queryRequest) {
        if (null == queryRequest) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        Page<Article> page = new Page<>(queryRequest.getPageNum(), queryRequest.getPageSize());
        String title = queryRequest.getTitle();
        String type = queryRequest.getType();
        String status = queryRequest.getStatus();
        String categoryId = queryRequest.getCategoryId();
        LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<Article>()
                .like(isNotBlank(title), Article::getTitle, title)
                .eq(isNotBlank(type), Article::getType, type)
                .eq(isNotBlank(status), Article::getStatus, status)
                .eq(isNotBlank(categoryId), Article::getCategoryId, categoryId)
                .orderByAsc(Article::getOrderNum);
        // 时间范围参数
        QueryWrapperUtils.addTimeRangeCondition(queryWrapper, queryRequest.getCreateTimeRange(), Article::getCreateOn);
        QueryWrapperUtils.addTimeRangeCondition(queryWrapper, queryRequest.getUpdateTimeRange(), Article::getUpdateOn);
        articleMapper.selectPage(page, queryWrapper);
        // 填充作者、分类信息
        List<Article> records = page.getRecords();
        List<ArticleVO> vos = new ArrayList<>();
        for (Article record : records) {
            ArticleVO vo = new ArticleVO();
            BeanUtil.copyProperties(record, vo);
            filleInfo2VO(record, vo);
            // 获取标签信息
            List<Tag> tags = articleTagMapper.selectList(new LambdaQueryWrapper<ArticleTag>()
                            .eq(ArticleTag::getArticleId, vo.getId())).stream()
                    .map(item -> tagMapper.selectById(item.getTagId())).filter(Objects::nonNull)
                    .collect(Collectors.toList());
            vo.setTags(tags);
            vos.add(vo);
        }
        // 显式构建安全分页对象，避免BeanUtil潜在问题
        Page<ArticleVO> voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal(), page.searchCount());
        voPage.setRecords(vos);
        // 设置总页数
        voPage.setPages(page.getPages());
        return voPage;
    }

    @Override
    public Boolean add(ArticleAddRequest addRequest) {
        // 必填项校验
        if (null == addRequest) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        String title = addRequest.getTitle();
        String type = addRequest.getType();
        if (StrUtils.isAnyBlank(title, type)) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        // 默认值填充
        String status = addRequest.getStatus();
        Article article = new Article();
        BeanUtil.copyProperties(addRequest, article);
        if (StrUtil.isEmpty(status)) {
            article.setStatus(CommonConstants.STATUS_ENABLE);
        }
        if (null == addRequest.getOrderNum()) {
            article.setOrderNum(CommonConstants.DEFAULT_ORDER_NUM);
        }
        // 保存文章（唯一性由数据库层面进行控制）
        boolean save = false;
        try {
            save = this.save(article);
        } catch (DuplicateKeyException e) {
            throw new BussinessException(ErrorCode.DATA_ALREADY_EXISTED);
        }
        return save;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean edit(ArticleUpdateRequest updateRequest) {
        if (null == updateRequest || StrUtil.isEmpty(updateRequest.getId())) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        // 更新文章内容
        ArticleContent articleContent = new ArticleContent();
        articleContent.setContent(updateRequest.getContent());
        articleContentMapper.update(articleContent, new LambdaQueryWrapper<ArticleContent>()
                .eq(ArticleContent::getArticleId, updateRequest.getId()));
        // 标签信息处理
        dealTagInfo(updateRequest.getId(), updateRequest.getTags());
        // 更新文章信息
        Article article = new Article();
        BeanUtil.copyProperties(updateRequest, article);
        return this.updateById(article);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean delete(BatchRequest batchRequest) {
        if (null == batchRequest || CollUtil.isEmpty(batchRequest.getIds())) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        // 删除对应的文章内容
        List<String> ids = batchRequest.getIds();
        articleContentMapper.delete(new LambdaQueryWrapper<ArticleContent>().in(ArticleContent::getArticleId, ids));
        // 删除文章信息
        return this.removeBatchByIds(ids);
    }

    @Override
    public Boolean editOrder(OrderModRequest orderModRequest) {
        if (null == orderModRequest) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        String id = orderModRequest.getId();
        Integer orderNum = orderModRequest.getOrderNum();
        if (StrUtil.isEmpty(id) || null == orderNum) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        LambdaUpdateWrapper<Article> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(Article::getOrderNum, orderNum)
                .eq(BasePO::getId, id);
        return this.update(updateWrapper);
    }

    @Override
    public Boolean updateRecommend(ArticleRecommendRequest recommendRequest) {
        if (null == recommendRequest) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        String id = recommendRequest.getId();
        String isRecommend = recommendRequest.getIsRecommend();
        if (CommonConstants.YES.equals(isRecommend)) {
            // 查询当前已推荐数量
            Long count = articleMapper.selectCount(new LambdaQueryWrapper<Article>()
                    .eq(Article::getIsRecommend, CommonConstants.YES)
                    .ne(BasePO::getId, id)
            );
            if (count >= 4) {
                throw new BussinessException("最多只允许推荐4篇文章");
            }
        }
        // 更新文章推荐状态
        LambdaUpdateWrapper<Article> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(Article::getIsRecommend, isRecommend)
                .eq(BasePO::getId, id);
        return this.update(updateWrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String publish(ArticlePublishRequest publishRequest) {
        if (null == publishRequest) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        // 必填项校验
        String title = publishRequest.getTitle();
        String categoryId = publishRequest.getCategoryId();
        String type = publishRequest.getType();
        String status = publishRequest.getStatus();
        Integer orderNum = publishRequest.getOrderNum();
        String content = publishRequest.getContent();
        if (StrUtils.isAnyBlank(title, categoryId, type, status, content) || null == orderNum) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        Article article = new Article();
        BeanUtil.copyProperties(publishRequest, article);
        // 默认值填充
        String description = article.getDescription();
        if (StrUtil.isEmpty(description)) {
            // todo: AI自动生成文章摘要
            article.setDescription("默认摘要...");
        }
        article.setViewsNum(0L);
        article.setCommentsNum(0L);
        article.setLikeNum(0L);
        // 插入文章数据
        this.save(article);
        publishRequest.setId(article.getId());
        // 插入文章内容数据
        ArticleContent articleContentPo = new ArticleContent();
        articleContentPo.setArticleId(article.getId());
        articleContentPo.setContent(content);
        articleContentMapper.insert(articleContentPo);
        // 标签信息处理
        dealTagInfo(publishRequest.getId(), publishRequest.getTags());
        return article.getId();
    }

    /**
     * 导出文章为markdown文件，并响应前端进行下载
     */
    @Override
    public void exportMarkdown(SingleRequest singleRequest, HttpServletResponse response) {
        if (null == singleRequest || StrUtil.isEmpty(singleRequest.getId())) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        Article articlePo = this.getById(singleRequest.getId());
        if (null == articlePo) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        ArticleContent articleContentPo = articleContentMapper.selectOne(new LambdaQueryWrapper<ArticleContent>()
                .eq(ArticleContent::getArticleId, singleRequest.getId()));
        if (null == articleContentPo) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        String markdownContent = articleContentPo.getContent();
        try {
            String fileName = articlePo.getTitle() + ".md";
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment; filename=" + URLUtil.encode(fileName));
            response.getOutputStream().write(markdownContent.getBytes(StandardCharsets.UTF_8));
            response.getOutputStream().flush();
            response.getOutputStream().close();
        } catch (IOException e) {
            throw new BussinessException(ErrorCode.FILE_DEAL_ERROR);
        }
    }

    /**
     * 标签信息处理
     *
     * @param articleId 文章id
     * @param tagNames  标签名集合
     */
    private void dealTagInfo(String articleId, List<String> tagNames) {
        if (CollectionUtil.isEmpty(tagNames)) {
            return;
        }
        List<Tag> tags = tagMapper.selectList(new LambdaQueryWrapper<Tag>()
                .in(Tag::getName, tagNames)
                .eq(Tag::getStatus, STATUS_ENABLE)
        );
        // 映射为Map
        Map<String, String> map = new HashMap<>();
        for (Tag tagPo : tags) {
            map.put(tagPo.getName(), tagPo.getId());
        }
        // 若标签不存在，则先新建
        for (String tag : tagNames) {
            if (map.containsKey(tag)) {
                continue;
            }
            Tag tagPo = new Tag();
            tagPo.setName(tag);
            tagPo.setDescription("自动创建");
            tagPo.setStatus(STATUS_ENABLE);
            tagMapper.insert(tagPo);
            map.put(tag, tagPo.getId());
        }
        // 删除已有关联关系
        articleTagMapper.delete(new LambdaQueryWrapper<ArticleTag>()
                .eq(ArticleTag::getArticleId, articleId));
        // 建立关联关系
        for (String tagId : map.values()) {
            ArticleTag articleTagPo = new ArticleTag();
            articleTagPo.setTagId(tagId);
            articleTagPo.setArticleId(articleId);
            articleTagMapper.insert(articleTagPo);
        }
    }

    // endregion

    // region 前台接口

    @Override
    public Page<ArticleVO> getListByCid(ArticleQueryRequest queryRequest) {
        if (null == queryRequest) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        Page<Article> page = new Page<>(queryRequest.getPageNum(), queryRequest.getPageSize());
        String categoryId = queryRequest.getCategoryId();
        if (StrUtil.isEmpty(categoryId)) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        articleMapper.selectPage(page, new LambdaQueryWrapper<Article>()
                .eq(!"all".equals(categoryId), Article::getCategoryId, categoryId)
                .ne(Article::getType, ArticleConstants.ARTICLE_TYPE_PRIVATE)
                .eq(Article::getStatus, ArticleConstants.ARTICLE_STATUS_PUBLISH)
                .eq(Article::getIsRecommend, CommonConstants.NO)
                .orderByAsc(Article::getOrderNum)
                .orderByDesc(Article::getCreateOn)
        );
        // 填充作者、分类信息
        List<Article> records = page.getRecords();
        List<ArticleVO> voList = new ArrayList<>();
        for (Article record : records) {
            // 封装 VO 实体
            ArticleVO vo = new ArticleVO();
            BeanUtil.copyProperties(record, vo);
            filleInfo2VO(record, vo);
            voList.add(vo);
        }
        // 显式构建安全分页对象，避免BeanUtil潜在问题
        Page<ArticleVO> voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal(), page.searchCount());
        voPage.setRecords(voList);
        // 设置总页数
        voPage.setPages(page.getPages());
        return voPage;
    }

    @Override
    public List<ArticleVO> getRandomList() {
        // 随机获取5篇文章
        int randomNum = 5;
        return articleMapper.selectList(new LambdaQueryWrapper<Article>()
                        .ne(Article::getType, ArticleConstants.ARTICLE_TYPE_PRIVATE)
                        .eq(Article::getStatus, ArticleConstants.ARTICLE_STATUS_PUBLISH)
                        .eq(Article::getCoverStyle, ArticleConstants.ARTICLE_COVER_STYLE_SMALL)
                        .last("ORDER BY RAND() LIMIT " + randomNum))
                .stream().map(article -> {
                    ArticleVO vo = new ArticleVO();
                    BeanUtil.copyProperties(article, vo);
                    filleInfo2VO(article, vo);
                    return vo;
                }).collect(Collectors.toList());
    }

    @Override
    public List<ArticleVO> getRecommendList() {
        // 获取推荐数据
        return articleMapper.selectList(new LambdaQueryWrapper<Article>()
                .ne(Article::getType, ArticleConstants.ARTICLE_TYPE_PRIVATE)
                .eq(Article::getStatus, ArticleConstants.ARTICLE_STATUS_PUBLISH)
                .ne(Article::getCoverStyle, ArticleConstants.ARTICLE_COVER_STYLE_NONE)
                .eq(Article::getIsRecommend, CommonConstants.YES)
        ).stream().map(article -> {
            // 填充作者、分类信息
            ArticleVO vo = new ArticleVO();
            BeanUtil.copyProperties(article, vo);
            filleInfo2VO(article, vo);
            return vo;
        }).collect(Collectors.toList());
    }

    @Override
    public List<ArticleVO> getTimeLineList() {
        // 根据时间顺序排序
        return articleMapper.selectList(new LambdaQueryWrapper<Article>()
                        .ne(Article::getType, ArticleConstants.ARTICLE_TYPE_PRIVATE)
                        .eq(Article::getStatus, ArticleConstants.ARTICLE_STATUS_PUBLISH)
                        .orderByDesc(Article::getCreateOn, Article::getOrderNum))
                .stream().map(article -> {
                    ArticleVO vo = new ArticleVO();
                    BeanUtil.copyProperties(article, vo);
                    filleInfo2VO(article, vo);
                    return vo;
                }).collect(Collectors.toList());
    }

    @Override
    public boolean tryLike(String ip, LikeRequest likeRequest) {
        if (null == likeRequest || StrUtil.isBlank(ip)) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        String contentId = likeRequest.getContentId();
        String likeType = likeRequest.getLikeType();
        // 获取点赞键前缀
        String keyPrefix = LikeEnum.getKeyPrefix(likeType);
        boolean tried = likeService.tryLike(keyPrefix, ip, contentId);
        if (!tried) {
            return false;
        }
        // 更新数据库点赞数量
        if (LikeEnum.ARTICLE_LIKE_PREFIX.getLikeType().equals(likeType)) {
            articleMapper.modLikeNums(contentId, 1);
        } else if (LikeEnum.COMMENTS_LIKE_PREFIX.getLikeType().equals(likeType)) {
            commentsMapper.modLikeNums(contentId, 1);
        }
        return true;
    }

    @Override
    public boolean cancelLike(String clientIp, LikeRequest likeRequest) {
        if (null == likeRequest || StrUtil.isBlank(clientIp)) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        String contentId = likeRequest.getContentId();
        String likeType = likeRequest.getLikeType();
        // 获取点赞键前缀
        String keyPrefix = LikeEnum.getKeyPrefix(likeType);
        boolean cancelled = likeService.cancelLike(keyPrefix, clientIp, contentId);
        if (!cancelled) {
            return false;
        }
        // 更新数据库点赞数量
        if (LikeEnum.ARTICLE_LIKE_PREFIX.getLikeType().equals(likeType)) {
            articleMapper.modLikeNums(contentId, -1);
        } else if (LikeEnum.COMMENTS_LIKE_PREFIX.getLikeType().equals(likeType)) {
            commentsMapper.modLikeNums(contentId, -1);
        }
        return true;
    }

    @Override
    public ArticleCardVO getCardInfo(SingleRequest singleRequest, HttpServletRequest request) {
        // 根据id获取文章信息
        String id = singleRequest.getId();
        if (StrUtil.isBlank(id)) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE, "获取作者卡片失败！");
        }
        Article article = articleMapper.selectById(id);
        // 查询所有公开/加密文章
        List<Article> articleList = articleMapper.selectList(new LambdaQueryWrapper<Article>()
                .eq(Article::getStatus, ArticleConstants.ARTICLE_STATUS_PUBLISH)
                .ne(Article::getType, ArticleConstants.ARTICLE_TYPE_PRIVATE)
                .orderByDesc(BasePO::getCreateOn));
        // 获取文章作者
        User user = userMapper.selectById(article.getCreateBy());
        if (null == user) {
            throw new BussinessException(ErrorCode.RESULT_DATA_NONE, "获取作者信息失败！");
        }
        // 统计标签数、分类数
        long categoryCount = 0;
        long tagCount = 0;
        if (CollUtil.isNotEmpty(articleList)) {
            Set<String> ids = articleList.stream().map(BasePO::getId).collect(Collectors.toSet());
            tagCount = articleTagMapper.selectList(new LambdaQueryWrapper<ArticleTag>()
                    .in(ArticleTag::getArticleId, ids)).stream().map(ArticleTag::getTagId).distinct().count();
            categoryCount = articleList.stream().map(Article::getCategoryId).distinct().count();
        }
        ArticleCardVO articleCardVO = new ArticleCardVO();
        articleCardVO.setNickname(user.getNickname());
        articleCardVO.setUserTag(user.getTagInfo());
        articleCardVO.setSign(user.getSign());
        articleCardVO.setAvatarUrl(user.getAvatarUrl());
        articleCardVO.setArticleCount((long) articleList.size());
        articleCardVO.setTagCount(tagCount);
        articleCardVO.setCategoryCount(categoryCount);
        articleCardVO.setLikeCount(article.getLikeNum());
        String keyPrefix = LikeEnum.getKeyPrefix("0");
        articleCardVO.setIsLike(likeService.hasLikedToday(keyPrefix, IpUtils.getClientIp(request), id));
        // 获取上一篇和下一篇文章
        fillNeighborArticles(articleList, id, articleCardVO);
        return articleCardVO;
    }

    @Override
    public String autoSummary(ArticleAutoSummaryRequest autoSummaryRequest) {
        String id = autoSummaryRequest.getId();
        String content = autoSummaryRequest.getContent();
        if (StrUtil.isBlank(content)) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE, "自动生成摘要失败！");
        }
        // 数据清洗(移除markdown语法，保留基本的段落结构)
        String cleanText = MarkdownCleaner.cleanMarkdownKeepParagraphs(content);
        String stats = MarkdownCleaner.getCleaningStats(content, cleanText);
        log.info("清洗统计：{}", stats);
        String summary = generateSummary(cleanText);
        if (StrUtil.isBlank(id)) {
            // id为空，仅返回摘要
            return summary;
        }
        // 更新数据库
        articleMapper.update(new LambdaUpdateWrapper<Article>()
                .set(Article::getDescription, summary)
                .set(BasePO::getUpdateOn, LocalDateTime.now())
                .eq(BasePO::getId, id)
        );
        // 返回摘要
        return summary;
    }

    /**
     * 生成AI摘要
     */
    private String generateSummary(String cleanText) {
        String summary = "";
        // 配置了AI相关信息
        if (StrUtil.isNotBlank(apiKey)) {
            // 超过3000字自动截断
            if (cleanText.length() > 3000) {
                cleanText = cleanText.substring(0, 3000);
            } else if (cleanText.length() < 100) {
                // 小于100则不生成
                throw new BussinessException(ErrorCode.FAIL, "文章内容过短，生成摘要失败!");
            }
            summary = ChatUtils.getSummary(apiKey, cleanText);
        } else {
            // 本地生成摘要
            summary = LocalSummaryGenerator.generateSmartSummary(cleanText, null);
        }
        if (StrUtil.isBlank(summary)) {
            throw new BussinessException(ErrorCode.FAIL, "文章内容无实际意义，生成摘要失败!");
        }
        return summary;
    }

    /**
     * 为文章卡片VO填充上一篇和下一篇信息
     *
     * @param articleList      已排序的文章列表（按创建时间降序）
     * @param currentArticleId 当前文章ID
     * @param articleCardVO    需要填充的VO对象
     */
    public void fillNeighborArticles(List<Article> articleList, String currentArticleId, ArticleCardVO articleCardVO) {
        if (CollUtil.isEmpty(articleList)) {
            setNoNeighborArticles(articleCardVO);
            return;
        }

        // 查找当前文章在列表中的索引位置
        int currentIndex = findCurrentArticleIndex(articleList, currentArticleId);

        if (currentIndex == -1) {
            // 当前文章不在列表中
            setNoNeighborArticles(articleCardVO);
            return;
        }

        // 获取上一篇和下一篇（基于降序排列的逻辑）
        Article prevArticle = getPreviousArticle(articleList, currentIndex);
        Article nextArticle = getNextArticle(articleList, currentIndex);

        // 填充VO对象
        setNeighborInfoToVO(articleCardVO, prevArticle, nextArticle);
    }

    /**
     * 查找当前文章在列表中的索引
     */
    private int findCurrentArticleIndex(List<Article> articleList, String currentArticleId) {
        for (int i = 0; i < articleList.size(); i++) {
            if (currentArticleId.equals(articleList.get(i).getId())) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 获取上一篇文章（时间更早的文章）
     * 由于列表是降序排列，上一篇应该是索引+1
     */
    private Article getPreviousArticle(List<Article> articleList, int currentIndex) {
        if (currentIndex < articleList.size() - 1) {
            return articleList.get(currentIndex + 1);
        }
        return null;
    }

    /**
     * 获取下一篇文章（时间更新的文章）
     * 由于列表是降序排列，下一篇应该是索引-1
     */
    private Article getNextArticle(List<Article> articleList, int currentIndex) {
        if (currentIndex > 0) {
            return articleList.get(currentIndex - 1);
        }
        return null;
    }

    /**
     * 将邻接文章信息设置到VO对象
     */
    private void setNeighborInfoToVO(ArticleCardVO vo, Article prevArticle, Article nextArticle) {
        // 设置上一篇信息
        if (prevArticle != null) {
            vo.setPrevId(prevArticle.getId());
            vo.setPrevTitle(prevArticle.getTitle());
        } else {
            vo.setPrevId(null);
            vo.setPrevTitle("已是最后一篇");
        }

        // 设置下一篇信息
        if (nextArticle != null) {
            vo.setNextId(nextArticle.getId());
            vo.setNextTitle(nextArticle.getTitle());
        } else {
            vo.setNextId(null);
            vo.setNextTitle("已是第一篇");
        }
    }

    /**
     * 设置无邻接文章的状态
     */
    private void setNoNeighborArticles(ArticleCardVO vo) {
        vo.setPrevId(null);
        vo.setPrevTitle("无上一篇");
        vo.setNextId(null);
        vo.setNextTitle("无下一篇");
    }

    // endregion

    // region 私有方法
    private void filleInfo2VO(Article record, ArticleVO vo) {
        String categoryId = record.getCategoryId();
        String authorId = record.getCreateBy();
        User user = userMapper.selectById(authorId);
        if (null != user) {
            vo.setAuthor(new SafeUserVO.Builder().from(user).desensitizeEmail().desensitizePhone().build());
        }
        Category category = categoryMapper.selectById(categoryId);
        if (null != category) {
            vo.setCategory(category);
        }
        String ip = IpUtils.getClientIp(httpServletRequest);
        // 填充当前文章是否被点赞
        boolean isLike = likeService.hasLikedToday(LikeEnum.getKeyPrefix("0"), ip, record.getId());
        vo.setLike(isLike);
    }

    // endregion

}